/*
Copyright 2017 Ziadin Givan

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

https://github.com/givanz/VvvebJs
*/

let Input = {
	
	init: function(name) {
	},


	onChange: function(event, node, input) {
		if (event && event.target) {
			const e = new CustomEvent('propertyChange', { detail: {value : input.value ?? this.value, input: this, origEvent:event} });
			event.currentTarget.dispatchEvent(e);
		}
	},

	renderTemplate: function(name, data) {
		return tmpl("vvveb-input-" + name, data);
	},

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('input');
		
			if (input) { 
				input.value = value;
			}
		}
	},
	
	render: function(name, data) {
		let html = this.renderTemplate(name, data);
		this.element = generateElements(html);
		
		//bind events
		if (this.events)
		for (let i in this.events) {
			ev = this.events[i][0];
			fun = this[ this.events[i][1] ];
			el = this.events[i][2];
		
			this.element[0].addEventListener(ev, function (ev, el, fun, target, event) {
			  if (event.target.closest(el)) {
				  //target, event, element, input
				return fun.call(event.target, event, target, this);
			  }
			}.bind(this, ev, el, fun, this.element[0]));		
		}

		return this.element[0];
	}
};

let TextInput = { ...Input, ...{

    events: [
        //event, listener, child element
        ["focusout", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let TextareaInput = { ...Input, ...{

    events: [
        ["keyup", "onChange", "textarea"],
	 ],
	
	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('textarea');
		
			if (input) { 
				input.value = value;
			}
		}
	},
	
	init: function(data) {
		return this.render("textareainput", data);
	},
  }	
}

let CheckboxInput = { ...Input, ...{

    events: [
        ["change", "onCheck", "input"],
	 ],
	
	onCheck: function(event, node, input) {
		input.value = this.checked;
		return input.onChange.call(this, event, node, input);
	},

	setValue: function(value) {
		if (this.element[0]) {
			let input = this.element[0].querySelector('input');

			if (input) { 
				if (value) {
					input.checked = true;
				} else {
					input.checked = false;
				}
			}
		}
	},
	
	init: function(data) {
		return this.render("checkboxinput", data);
	},
  }
}

let SelectInput = { ...Input, ...{
	
    events: [
        ["change", "onChange", "select"],
	 ],
	

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('select');
		
			if (input) { 
				input.value = value;
			}
		}
	},
	
	init: function(data) {
		return this.render("select", data);
	},
  }
}

let IconSelectInput = { ...Input, ...{
	
    events: [
        ["change", "onChange", "select"],
	 ],
	

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('select');
		
			if (input) { 
				input.value = value;
			}
		}
	},
	
	init: function(data) {
		return this.render("icon-select", data);
	},
  }
}

let HtmlListSelectInput = { ...Input, ...{
	
	data:{},
	cache:{},
	
    events: [
        //["click", "onChange", "li"],
        ["change", "onListChange", "select"],
        ["keyup", "searchElement", "input.search"],
        ["click", "clearSearch", "button.clear-backspace"],
	 ],
	
	clearSearch : function(event, element, input) {
		let search = element.querySelector("input.search");
		if (search) {
			search.value = "";
			input.searchElement(event, element, input);
		}
		
		search.dispatchEvent(new KeyboardEvent("keyup", {
			bubbles: true,
			cancelable: true,
		}));
	},


	searchElement : function(event, element, input) {
		searchText = this.value;
		
		delay(() => {
			element.querySelectorAll("li").forEach((el, i) => {
				
				if (!searchText || el.title.indexOf(searchText) > -1) { 
					el.style.display = '';
				} else {
					el.style.display = 'none';
				}
			});
			
		}, 500);
	},	

	onElementClick: function(event, element, input) {
		let data = input.data;
		let svg = this.closest(data.insertElement);
		let value = svg.outerHTML ?? "<svg></svg>";
		input.value = value;
		let ret = input.onChange.call(this, event, element, input);
		
		return element;
	},
	
	onListChange: function(event, element, input) {
		let url = input.data.url.replace('{value}', this.value);
		let elements = element.querySelector(".elements");
		
		elements.innerHTML = `<div class="p-4"><div class="spinner-border spinner-border-sm" role="status">
		  <span class="visually-hidden">Loading...</span>
		</div> Loading...</div>`;
		
		//cache ajax requests
		if (input.cache[url] != undefined) {
			elements.innerHTML = input.cache[url];
		} else {
			fetch(url)
			.then((response) => {
				if (!response.ok) { throw new Error(response) }
				return response.text()
			})
			.then((text) => {
					input.cache[url] = text;
					elements.innerHTML = text;
			})
			.catch(error => {
				console.log(error.statusText);
				displayToast("bg-danger", "Error", "Error loading list");
			});
		}
	},
	
	setValue: function(value) {
		let select = this.element[0].querySelector("select");
		if (value && select) {
			select.value = value;
		}
	},
	
	init: function(data) {
		this.data = data;
		this.events.push(["click", "onElementClick", data.clickElement]);
		let template = this.render("html-list-select", data);
		let select = template.querySelector("select");
		this.onListChange.call(select, new Event('change'), template, this);
		return template;
	},
  }
}

let LinkInput = { ...TextInput, ...{

    events: [
        ["change", "onChange", "input"],
	 ],
	/*
	setValue: function(value) {
		//value = value.replace(/(?<!\/)www\./, 'https://www.');
		this.element.querySelector('input').value = value;
	},
	*/
	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let DateInput = { ...TextInput, ...{

    events: [
        ["change", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("dateinput", data);
	},
  }
}

let RangeInput = { ...Input, ...{

    events: [
        ["change", "onRangeChange", "input"],
	 ],
	 
	onRangeChange: function(event, node, input) {
		this.parentNode.querySelector('input[type=number]').value = this.value;
		this.parentNode.querySelector('input[type=range]').value = this.value;
		return input.onChange.call(this, event, node, input);
	},	 

	setValue: function(value) {
		this.element[0].querySelector('input[type=number]').value = value;
		this.element[0].querySelector('input[type=range]').value = value;
	},
	
	init: function(data) {
		return this.render("rangeinput", data);
	},
  }
}

let NumberInput = { ...Input, ...{

    events: [
        ["change", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("numberinput", data);
	},
  }
}

let CssUnitInput = { ...Input, ...{

	number:0,
	unit:"px",

    events: [
        ["change", "onInputChange", "select"],
        ["change", "onInputChange", "input"],
        ["keyup", "onInputChange", "input"],
	 ],
		
	onInputChange: function(event, node, input) {
		if (node) {
			let number = node.querySelector("input").value;
			let unit = node.querySelector("select").value;

			if (this.value != "") input[this.name] = this.value;// this.name = unit or number	
			if (unit == "") unit = "px";//if unit is not set use default px
		
			let value = "";	
			if (unit == "auto")  {
				node.classList.add("auto");
				value = unit;
			}
			else  {
				node.classList.remove("auto");
				value = number + (unit ? unit : "");
			}
			
			input.value = value;

			return input.onChange.call(this, event, node, input);
		}
	},
	
	setValue: function(value) {
		if (value && this.element) {
			let element = this.element[0];
			this.number = parseFloat(value);
			this.unit = value.replace(this.number, '').trim();
			
			if (this.unit == "auto") element.classList.add("auto");

			element.querySelector("input[type=number]").value = this.number;
			element.querySelector("select").value = this.unit;
		}
	},
	
	init: function(data) {
		return this.render("cssunitinput", data);
	},
  }
}

let ColorInput = { ...Input, ...{

	 //html5 color input only supports setting values as hex colors even if the picker returns only rgb
	 rgb2hex: function(value) {
		 if (value) {
			 value = value.trim();
			 
			 if (rgb = value.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i)) {
				 
				 return (rgb && rgb.length === 4) ? "#" +
				  ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
				  ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
				  ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : rgb;
			 }
		}
		 
		 return value;
	},

    events: [
        ["change", "onChange", "input"],
	 ],

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('input');
		
			if (input) { 
				input.value = this.rgb2hex(value);
			}
		}
	},
	
	init: function(data) {
		//if no palette provided use default
		if (!data.palette) {
			data.palette = Vvveb.ColorPalette.getAll();
		}
		
		return this.render("colorinput", data);
	},
  }
}

let ImageInput = { ...Input, ...{

    events: [
        ["focusout", "onChange", "input[type=text]"],
        ["change", "onUpload", "input[type=file]"],
	 ],

	setValue: function(value) {

		//don't set blob value to avoid slowing down the page		
		if (value.indexOf("data:image") == -1) {
			element.querySelector('input[type="text"]').value = value;
		}
	},

	onUpload: function(event, node) {

		if (this.files && this.files[0]) {
            let reader = new FileReader();
            reader.onload = imageIsLoaded;
            reader.readAsDataURL(this.files[0]);
            //reader.readAsBinaryString(this.files[0]);
            file = this.files[0];
        }

		function imageIsLoaded(e) {
				
				image = e.target.result;
				
				event.data.element.trigger('propertyChange', [image, this]);
				
				//return;//remove this line to enable php upload

				let formData = new FormData();
				formData.append("file", file);
    
				$.ajax({
					type: "POST",
					url: 'upload.php',//set your server side upload script url
					data: formData,
					processData: false,
					contentType: false,
					success: function (data) {
						console.log("File uploaded at: ", data);
						
						//if image is succesfully uploaded set image url
						event.data.element.trigger('propertyChange', [data, this]);
						
						//update src input
						event.data.element.querySelector('input[type="text"]').value = data;
					},
					error: function (data) {
						alert(data.responseText);
					}
				});		
		}
	}		
 }	
}

let FileUploadInput = { ...TextInput, ...{

    events: [
        ["focusout", "onChange", "input"],
	 ],

	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let RadioInput = { ...Input, ...{

	events: [
        ["change", "onChange", "input"],
	 ],

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('input');
		
			if (input) { 
				if (value == input.value) {
					input.setAttribute("checked", "true");
					input.checked = true;
				} else {
					input.checked = false;
					input.removeAttribute("checked");
				}
				
			}
		}
	},
	
	init: function(data) {
		return this.render("radioinput", data);
	},
  }
}

let RadioButtonInput = { ...RadioInput, ...{

	setValue: function(value) {
		if (this.element[0] && value) {
			let inputs = this.element[0].querySelectorAll('input');
			inputs.forEach((el, i) => {
				if (value == el.value) {
					selected = el;
				} else {
					el.checked = false;
					el.removeAttribute("checked");
				}		
			});

			selected.checked = true;
			selected.setAttribute("checked", "checked");
		}
	},

	init: function(data) {
		return this.render("radiobuttoninput", data);
	},
  }
}

let ToggleInput = { ...Input, ...{
	events: [
        ["change", "onToggleChange", "input"],
	 ],

	onToggleChange: function(event, node, input) {
		input.value = this.checked ? this.getAttribute("data-value-on") : this.getAttribute("data-value-off");
		return input.onChange.call(this, event, node, input);
	},

	setValue: function(value) {
		if (this.element[0]) {
			let input = this.element[0].querySelector('input');

			if (input) { 
				if (value == input.getAttribute("data-value-on")) {
					input.checked = true;
					input.setAttribute("checked", true);
				} else {
					input.checked = false;
					input.removeAttribute("checked");
				}
			}
		}
	},
	
	init: function(data) {
		return this.render("toggle", data);
	},
  }
}

let ValueTextInput = { ...TextInput, ...{

    events: [
        ["focusout", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let GridLayoutInput = { ...TextInput, ...{

    events: [
        ["focusout", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let ProductsInput = { ...TextInput, ...{

    events: [
        ["focusout", "onChange", "input"],
	 ],
	
	init: function(data) {
		return this.render("textinput", data);
	},
  }
}

let GridInput = { ...Input, ...{
	

    events: [
        ["change", "onChange", "select" /*'select'*/],
        ["click", "onChange", "button" /*'select'*/],
	 ],
	

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('select');
		
			if (input) { 
				input.value = value;
				input.querySelector("option[selected]").selected = true;
			}
		}
	},
	
	init: function(data) {
		return this.render("grid", data);
	},
  }
}

let TextValueInput = { ...Input, ...{
	

    events: [
        ["focusout", "onChange", "input"],
	    ["click", "onChange", "button" /*'select'*/],
	 ],
	
	setValue: function(value) {
		return false;
	},
		
	init: function(data) {
		return this.render("textvalue", data);
	},
  }
}

let ButtonInput = { ...Input, ...{

    events: [
        ["click", "onChange", "button" /*'select'*/],
	 ],
	

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('button');
		
			if (input) { 
				input.value = value;
			}
		}		
	},
	
	init: function(data) {
		return this.render("button", data);
	},
  }
}

let SectionInput = { ...Input, ...{

    events: [
        //["click", "onChange", "button" /*'select'*/],
	 ],
	

	setValue: function(value) {
		return false;
	},
	
	init: function(data) {
		return this.render("sectioninput", data);
	},
  }
}

let ListInput = { ...Input, ...{
	
    events: [
        ["change", "onChange", "select"],
        ["click", "remove", ".delete-btn"],
        ["click", "add", ".btn-new"],
        ["click", "select", ".section-item"],
	 ],
	

	remove: function(event, node, input) {
		let sectionItem = this.closest(".section-item");
		let index = [...sectionItem.parentNode.children].indexOf(sectionItem);//sectionItem.index();
		let data = input.data;
		
		if (data.removeElement) {
			let container = input.node;
			if (data.container) {
				container.querySelector(data.container);
			}
			container.querySelector(data.selector + ":nth-child(" + (index + 1) + ")").remove();
		}
		sectionItem.remove();
		
		event.action = "remove";
		event.index = index;
		input.onChange(event, node, input, this);
		event.preventDefault();
		return false;
	},

	add: function(event, node, input) {
		let newElement = input.data.newElement ?? false;
		if (newElement) {
			let container = input.node;
			if (data.container) {
				container.querySelector(data.container);
			}			
			container.append(generateElements(newElement)[0]);
		}
		
		event.action = "add";
		input.onChange(event, node, input, this);
		return false;
	},	
	
	select: function(event, node, input) {
		let sectionItem = this.closest(".section-item");
		if (sectionItem.parentNode) {
			let index = [...sectionItem.parentNode.children].indexOf(sectionItem);//sectionItem.index();
		
		event.action = "select";
		event.index = index;
		input.onChange(event, node, input, this);
		}
		return false;
	},

	setValue: function(value) {
	},

	init: function(data, node) {
		this.component = data.component;
		this.selector = data.selector;
		this.node = node;

		let elements = this.node.querySelectorAll(data.container + " " + this.selector);
		let options = [];
		
		elements.forEach(function (e, i) {
			let element = e;
			if (data.nameElement) {
				element = element.querySelector(data.nameElement);
			}
			let name = (data.name = "text" ? element.textContent.substr(0, 15) : element.id);
			options.push({
				name: name,
				type: (data.prefix ?? "") + (i + 1) + (data.suffix ?? ""),
			});
		});

		data.options = options;
		data.elements = elements;
		this.data = data;

		return this.render("listinput", data);
	},
  }
}

let AutocompleteInput = { ...Input, ...{

    events: [
        ["autocomplete.change", "onAutocompleteChange", "input"],
	 ],

	onAutocompleteChange: function(event, value, text) {
		input.value = value;
		return this.onChange(event, node);
	},

	init: function(data) {
		
		this.element = this.render("textinput", data);
		
		let autocomplete = new Autocomplete({input:this.element.querySelector("input"), url:data.url});
		
		return this.element;
	}
  }
}

let AutocompleteList = { ...Input, ...{

    events: [
        ["autocompletelist.change", "onAutocompleteChange", "input"],
	 ],

	onAutocompleteChange: function(event, node, input) {
		input.value = event.detail;
		return input.onChange.call(this, event, node, input);
	},

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('input');
		
			if (input) { 
				input.dataset.autocompleteList.setValue(value);
				
			}
		}		
	},

	init: function(data) {
		
		this.element = this.render("textinput", data);
		
		let autocomplete = new Autocomplete({input:this.element.querySelector("input"), url:data.url});
		$('input', this.element).autocompleteList(data);//using default parameters
		
		return this.element;
	}
  }
}

let TagsInput = { ...Input, ...{

    events: [
        ["tagsinput.change", "onTagsInputChange", "input"],
	 ],

	onTagsInputChange: function(event, value, text) {
		input.value = value;
		return this.onChange(event, node);
	},

	setValue: function(value) {
		if (this.element[0] && value) {
			let input = this.element[0].querySelector('select');
		
			if (input) { 
				input.dataset.tagsInput.setValue(value);
				
			}
		}
	},

	init: function(data) {
		
		this.element = this.render("tagsinput", data);
		
		$('input', this.element).tagsInput(data);//using default parameters
		
		return this.element;
	}
  }
}


let NoticeInput = { ...Input, ...{

    events: [
	 ],
	
	init: function(data) {
		return this.render("noticeinput", data);
	},
  }
}
